/********************************************************************************
*                 Copyright (C) 2011 - Las Venturas Playground                  *
*                      http://iv-mp.nl | http://lvp-iv.com                      *
*                                                                               *
*    This program is free software: you can redistribute it and/or modify       *
*    it under the terms of the GNU General Public License as published by       *
*    the Free Software Foundation, either version 3 of the License, or          *
*    (at your option) any later version.                                        *
*                                                                               *
*    This program is distributed in the hope that it will be useful,            *
*    but WITHOUT ANY WARRANTY; without even the implied warranty of             *
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
*    GNU General Public License for more details.                               *
*                                                                               *
*    You should have received a copy of the GNU General Public License          *
*    along with this program.  If not, see <http://www.gnu.org/licenses/>.      *
*                                                                               *
*********************************************************************************
*                                                                               *
*  Las Venturas Playground - Commands/commandsCore.nut                          *
*                                                                               *
*  This file will hold the Command handler core.                                *
*  Author: Pugwipe                                                              *
*                                                                               *
********************************************************************************/

/*
 * Class: CommandHandler
 * 
 * A command container class. You can register command objects with this class, and it will work its magic.
 */
class CommandHandler 
{
    /*
     * constructor
     */
    constructor ()
    {
        this.commands = {}
        this.commandLevel = {}
        this.registerCommandHandler (this);
    }
    
    /*
     * Registers a command object with this class. These can be normal commands, but also compound ones.
     *
     * @param commandTrigger - A string containing the command that will trigger the command. For example, "/help".
     * @param commandObject - The object that's associated with the command.
     */     
    function registerCommand (commandTrigger, commandObject, commandLevel)
    {    
        // Make it lowercase, and insert it into our table
        commandTrigger = commandTrigger.tolower ();
        this.commands[commandTrigger] <- commandObject;
        
        // Save the command level as well.
        this.commandLevel[commandTrigger] <- commandLevel.tointeger ();
    }
    
    /*
     * Method: onCommand
     * To be called when someone types a command.
     * 
     * @param playerid - The player who typed the command
     * @param commandString - The string that the player typed.
     * 
     * @return true if a command is available to the player, false otherwise.
     */
    function onCommand (playerid, commandString)
    {
        local commandWords = split (commandString, " ");
        local commandTrigger = commandWords[0].tolower ();
        
        // Check if the command exists in this handler
        if (commandTrigger in commands) 
        {
            // Why, it does!
            local commandObject = commands[commandTrigger];
            
            // Get the level
            local commandLevel = commandLevel[commandTrigger];
            
            // Execute it, and return the returned value
            return commandObject.execute (playerid, commandWords, commandString, commandLevel);
        }
        
        return false;
    }
    
    commands = null;
    commandLevel = null;
}

/* Class: CommandBase
 * 
 * The base class for every kind of command.
 */
class CommandBase 
{
    /**
     * This method is called whenever a player types the command associated with this object.
     * It MUST be overloaded in derived classes.
     *
     * @param playerid - The ID of the player who typed the command
     * @param commandWords - An array containing the space-separated parameters the player typed, including the command itself.
     * @param commandString - A string containing the whole command string.
     * @param commandLevel - An integer holding the access level required to execute the command.
     */
    function execute (playerid, commandWords, commandString, commandLevel) {}
}

/* Class: Command
 *
 * This class represents a basic command, available to all players. It simply stores a
 * function pointer that is called whenever someone types the command associated with this object.
 * If you want commands with limited access, you have to create a derived class
 * with an overloaded isAuthorized() method.
 */
class Command extends CommandBase
{
    /*
     * The constructor of the class.
     * 
     * @param callback - A function pointer that is called whenever this command is executed.
     *                   It must be of the format function (playerid, commandWords, commandString, ...)
     *                   where ... represent additional arguments that could potentially be passed.
     */
    constructor (callback)
    {
        if (type (callback) != "function") return;
        
        // Simply store it away
        _callback = callback;
    }
    
    /*
     * Called whenever this command is executed.
     *
     * @param playerid - The ID of the player who typed the command
     * @param commandWords - An array containing the space-separated parameters the player typed, including the command itself.
     * @param commandString   - A string containing the whole command string.
     *
     * @return Returns false if the command is unavailable to a player, true otherwise.
     */
    function execute (playerid, commandWords, commandString, commandLevel)
    {
        // Check if the player has access to this command
        if (!isAuthorized (playerid, commandLevel)) return false;
        
        // And simply call the callback function
        _callback (playerid, commandWords, commandString);
        return true;
    }
    
    /*
     * Determines whether a player has access to this type of command.
     * By default it returns true for all players, but this function can be overloaded in derived classes.
     *
     * @param playerid - The ID of the player
     * @param commandLevel - The level the command requires to be executed.
     *
     * @return True if the user has access, false otherwise.
     */     
    function isAuthorized (playerid, commandLevel)
    {
        if (commandLevel > playerInfo[playerid].Level) return false;
        return true;
    }
    
    _callback = null;
}

/* Class: CompoundCommand
 * 
 * Class for compound commands. Compound commands are commands that are actually
 * several commands packed into one, like /p id bla.
 */
class CompoundCommand extends CommandBase 
{
    /*
      * constructor
     */
    constructor ()
    {
        _subCommands = {};
    }
    
    /*
     * Adds a subcommand to the list.
     *
     * @param: subCmd - A string containing the subcommand, for example, "bla" for /p id bla.
     * @param: cmdObj - A command object.
     */
    function addSubCmd (subCmd, cmdObj, paramID = 2)
    {
        if (cmdObj instanceof CompoundCommand) return;
        
        if (!(paramID in _subCommands)) 
        {
            _subCommands[paramID] <- {};
        }
        
        subCmd = subCmd.tolower ();
        _subCommands[paramID][subCmd] <- cmdObj;
    }
    
    /*
     * Returns the command object associated with a subcommand.
     *     
     * @param: subCmd - A string containing the subcommand.
     * @return The command object associated with a sub command.
     */
    function getSubCmd (subCmd, paramID = 2)
    {
        if (! (paramID in _subCommands)) return null;
        if (! (subCmd in _subCommands[paramID])) return null;
        
        return _subCommands[paramID][subCmd];
    }
    
    /* 
     * Called when this command is executed. This method must be implemented in derived classes.
     * 
     * @param playerid - The ID of the player who typed the command
     * @param commandWords - An array containing the space-separated parameters the player typed, including the command itself.
     * @param commandString   - A string containing the whole command string.
     *
     */
    function execute (playerid, cmdWords, cmdStr) 
    {
        local len = cmdWords.len ();
        local subCmd = null;
        
        // Loop through the parameters and see if we run into a command
        for (local i = 1; i < len; i++) 
        {
            local word = cmdWords[i];
            subCmd = getSubCmd (word.tolower (), i);
            
            // Ok, we found one, let's exit the loop
            if (subCmd) break;
        }
        
        if (subCmd && subCmd.execute (playerid, cmdWords, cmdStr)) {
            // Found and executed the command successfully
            return true;
        }
        
        // Still here, so something went wrong.
        // Display the error string to the player
        sendPlayerMessage (playerid, getErrorString ());
        
        // We have to return true here, otherwise we will have double error messages
        return true;
    }

     
     /*
      * Method: setErrorString ()
      * Sets the error string when a player enters invalid parameters.
      *
      * @param errorStr - The error string that is printed in case of bad usage.
      */
     function setErrorString (errorStr)
     {
        _errorStr = errorStr;
     }
     
     function getErrorString ()
     {
        return _errorStr;
     }
     
     /*
      * Data members
      */    
    _errorStr = "Invalid usage.";
    _subCommands = null;
}

